#pragma once
#include <iostream>
#include <stdio.h>
#include "httplib.h"
#include <mysql.h>
#pragma comment(lib,"libmysql.lib")

#include <algorithm>
#include <vector>
#include <string>
#include <unordered_set>
#include <ctime>
#include <random>
#include <sstream>
#include <string.h>
#include <fstream>
#include <cstring>
#include <iomanip>
#include "paths.h"
using namespace std;
using namespace httplib;

class Server_END;
struct BanRecord;

class exam_aver
{
public:
	int order;
	float sub_aver;
	float ob_aver;
	float total_aver;
public:
	exam_aver(){}
	exam_aver(int order, float ob, float sub, float total)
	{
		this->order = order;
		this->ob_aver = ob;
		this->sub_aver = sub;
		this->total_aver = total;
	}
};

class ob_acc
{
public:
	int code;
	float acc;
	int order;
public:
	ob_acc()
	{
		acc = 0;
	}
	ob_acc(int code, float acc, int order)
	{
		this->code = code;
		this->acc = acc;
		this->order = order;
	}
};

class sub_acc
{
public:
	int code;
	float acc;
	int order;
public:
	sub_acc() 
	{
		acc = 0;
	}
    sub_acc(int code, float acc, int order)
	{
		this->code = code;
		this->acc = acc;
		this->order = order;
	}
};

class Date
{
public:
	int year;
	int month;
	int day;
	int hour;
	int minute;
	int second;
public:
	Date(){}
	Date(int y, int m, int d, int h, int mi, int sec)
	{
		this->year = y;
		this->month = m;
		this->day = d;
		this->hour = h;
		this->minute = mi;
		this->second = sec;
	}
	Date(int m, int d, int h, int mi)
	{
		this->year = 2024;
		this->month = m;
		this->day = d;
		this->hour = h;
		this->minute = mi;
		this->second = 0;
	}
	string to_string();
	Date add_minute(int);
};

Date getLocalDate() {
	time_t now;
	time(&now);
	tm t;
#ifdef _WIN32
	localtime_s(&t, &now);
#else
	localtime_r(&now, &t);
#endif
	Date result(t.tm_year + 1900, t.tm_mon + 1, t.tm_mday, t.tm_hour, t.tm_min, t.tm_sec);
	return move(result);
}

class Exam
{
public:
	int exam_code;
	string sub_list;
	string student_list;
	string subject;
	int duration;
	Date exam_date;
public:
	Exam(){}
	Exam(int eo, string sl, string stul, string sub, int d, Date ed)
	{
		this->exam_code = eo;
		this->sub_list = sl;
		this->student_list = stul;
		this->subject = sub;
		this->duration = d;
		this->exam_date = ed;
	}
	Exam(int eo, string sl, string sub, int d, Date ed)
	{
		this->exam_code = eo;
		this->sub_list = sl;
		this->student_list = "";
		this->subject = sub;
		this->duration = d;
		this->exam_date = ed;
	}
};

class Student_score
{
public:
	int student_id;
	int total_score;
	int object_score;
	int subject_score;
	string wrong_sub_code;
	int exam_order;
public:
	Student_score(){}
	Student_score(int id, int total, int object, int subject, string wrong_ans, int exam_order)
	{
		this->student_id = id;
		this->total_score = total;
		this->object_score = object;
		this->subject_score = subject;
		this->wrong_sub_code = wrong_ans;
		this->exam_order = exam_order;
	}
};

class Object_Sub
{
public:
	int code;
	int teacher_id;
	string subject;
	string describe;
	string Option_A;
	string Option_B;
	string Option_C;
	string Option_D;
	int ans;
public:
	Object_Sub(){}
	Object_Sub(int code,int teacher_id, string subject, string describe, string option_a, string option_b, string option_c, string option_d, int ans)
	{
		this->code = code;
		this->teacher_id = teacher_id;
		this->subject = subject;
		this->describe = describe;
		this->Option_A = option_a;
		this->Option_B = option_b;
		this->Option_C = option_c;
		this->Option_D = option_d;
		this->ans = ans;
	}
	Object_Sub(int code, string subject, string describe, string option_a, string option_b, string option_c, string option_d, int ans)
	{
		this->code = code;
		this->teacher_id = 0;
		this->subject = subject;
		this->describe = describe;
		this->Option_A = option_a;
		this->Option_B = option_b;
		this->Option_C = option_c;
		this->Option_D = option_d;
		this->ans = ans;
	}
};

class Subject_Sub
{
public:
	int code;
	int teacher_id;
	string subject;
	string describe;
public:
	Subject_Sub(){}

	Subject_Sub(int code, int teacher_id, string subject, string describe)
	{
		this->code = code;
		this->teacher_id = teacher_id;
		this->subject = subject;
		this->describe = describe;
	}

	Subject_Sub(int code, string subject, string describe) {
		this->code = code;
		this->teacher_id = 0;
		this->subject = subject;
		this->describe = describe;
	}
};

class Student_ob_ans
{
public:
	int stu_id;
	int sub_code;
	int option_ans;
	bool correct;
public:
	Student_ob_ans(){}
	Student_ob_ans(int id, int code, int ans)
	{
		this->stu_id = id;
		this->sub_code = code;
		this->option_ans = ans;
		this->correct = 0;
	}
};

class Student_sub_ans
{
public:
	int stu_id;
	int sub_code;
	string sub_ans;
	int score;
public:
	Student_sub_ans(){}
	Student_sub_ans(int id, int code, string ans)
	{
		this->stu_id = id;
		this->sub_code = code;
		this->sub_ans = ans;
		this->score = -1;
	}
};

class Student
{
public:
	int id;
	string name;
	string password;
	string pro;
	int class_id;
public:
	Student(){}

	Student(int id, string name, string password, string pro, int class_id)
	{
		this->id = id;
		this->name = name;
		this->password = password;
		this->pro = pro;
		this->class_id = class_id;
	}
};

class Teacher
{
public:
	int id;
	string name;
	string password;
	string subject;
public:
	Teacher(){}
	
	Teacher(int id, string name, string password, string subject)
	{
		this->id = id;
		this->name = name;
		this->password = password;
		this->subject = subject;
	}

	BanRecord ban(int, string);
};

struct BanRecord {
	int stu_id;
	int tea_id;
	Date ban_date;
	string reason;
	BanRecord() {}
	BanRecord(int stu_id, int tea_id, Date ban_date, string reason) {
		this->stu_id = stu_id;
		this->tea_id = tea_id;
		this->ban_date = ban_date;
		this->reason = reason;
	}
};

vector<string> get_all_from_jsonlist(string value, string json) {
	stringstream toVector(json);
	char c;
	string buf;
	vector<string> result;
	bool putin = false;
	bool pushback = false;
	bool testendchar = false;
	char endchar;
	while (toVector >> c) {
		if (c == '{' || c == '}') {
			putin = false;
			pushback = false;
			buf.clear();
			continue;
		}
		if (c == ']')
			break;
		if (putin == true) {
			if (testendchar == true) {
				buf.clear();
				if (c == '"')
					endchar = '"';
				else {
					endchar = ',';
					buf.push_back(c);
				}
				testendchar = false;
				pushback = true;
				continue;
			}
			if (pushback == false){
				if (c == ':')
					testendchar = true;
				continue;
			}
			if (c == endchar) {
				pushback = false;
				result.push_back(buf);
				putin = false;
			}
			else buf.push_back(c);
		}
		else{
			if (c == '"') {
				pushback = !pushback;
				if (pushback == false && buf == value)
					putin = true;
			}
			else {
				if (pushback == true)
					buf.push_back(c);
				else if (!buf.empty())
					buf.clear();
			}
		}
	}
	return result;
}
string get_list_from_json(string value, string json) {
	stringstream toString(json);
	char c;
	string buf, result;
	bool putin = false;
	bool pushback = false;
	while (toString >> c) {
		if (putin == true) {
			if (c == '[')
				pushback = true;
			if (pushback == true)
				result.push_back(c);
			if (c == ']')
				break;
		}
		else {
			if (c == '{') {
				buf.clear();
				continue;
			}
			if (c == '}')
				break;
			if(c == '"'){
				pushback = !pushback;
				if (pushback == false && buf == value)
					putin = true;
			}
			else {
				if (pushback == true)
					buf.push_back(c);
				else if (!buf.empty())
					buf.clear();
			}
		}
	}
	return result;
}
int get_first_scale_from_jsonlist(string json) {
	stringstream toInt(json);
	char c1, c2;
	c1 = c2 = ' ';
	int result = 0;
	bool match = false;
	while (toInt >> c2) {
		if (c2 == '{')
			match = true;
		else if (c2 == '}')
			break;
		if (match == true && c1 == '"' && c2 == ':')
			result++;
		c1 = c2;
	}
	return result;
}

time_t ToTimeT(int year, int month, int day, int hour, int minute)
{
	std::tm tm = {};
	tm.tm_year = year - 1900;
	tm.tm_mon = month - 1;
	tm.tm_mday = day;
	tm.tm_hour = hour;
	tm.tm_min = minute;
	tm.tm_sec = 0;

	time_t t = std::mktime(&tm);
	return t;
}

bool match_exam(Exam& t, string subject)
{
	time_t now = time(0);
	time_t start = ToTimeT(t.exam_date.year, t.exam_date.month, t.exam_date.day, t.exam_date.hour, t.exam_date.minute);
	time_t end = start + (t.duration + 5) * 60;
	if (now >= start && now <= end && t.subject == subject)
	{
		return true;
	}
	return false;
}

bool future_exam(Exam& t)
{
	time_t now = time(0);
	time_t end = ToTimeT(t.exam_date.year, t.exam_date.month, t.exam_date.day, t.exam_date.hour, t.exam_date.minute) + (t.duration + 5) * 60;
	if (now <= end)
	{
		return true;
	}
	return false;
}

bool IsGreater(Exam t1, Exam t2)
{
	time_t s1 = ToTimeT(t1.exam_date.year, t1.exam_date.month, t1.exam_date.day, t1.exam_date.hour, t1.exam_date.minute);
	time_t s2 = ToTimeT(t2.exam_date.year, t2.exam_date.month, t2.exam_date.day, t2.exam_date.hour, t2.exam_date.minute);
	if (s1 > s2)
	{
		return true;
	}
	return false;
}

class MySQL
{
private:
	MYSQL* SQL;
public:
	friend class Server_END;

	void create_DataBase();
	void init_sql();
	void insert_sql(Student &s);
	void insert_sql(Teacher &t);
	void insert_sql(Object_Sub& t);
	void insert_sql(Subject_Sub& t);
	void insert_sql(Student_ob_ans& t);
	void insert_sql(Student_sub_ans& t);
	void insert_sql(Student_score& t);
	void insert_sql(ob_acc& t);
	void insert_sql(sub_acc& t);
	void insert_sql(Exam& t);
	void insert_sql(exam_aver& t);
	void insert_sql(BanRecord& t);

	void delete_stu(int id);
	void delete_stu();
	void delete_tea(int id);
	void delete_tea();
	void delete_ob(int code);
	void delete_sub(int code);
	void delete_ob_ans();
	void delete_sub_ans();
	void delete_exam(int order);
	void delete_ban_record(int id);

	void modify_sql(Student& t);
	void modify_sql(Teacher& t);
	void modify_sql(Object_Sub& t);
	void modify_sql(Subject_Sub& t);
	void modify_sql(Exam& t);

	vector<Student> search_stu(string str);
	vector<Student> search_stu(int id);
	vector<Student> search_stu();
	vector<Teacher> search_tea(string str);
	vector<Teacher> search_tea(int id);
	vector<Teacher> search_tea();
	vector<Object_Sub> search_ob();
	vector<Object_Sub> search_ob(int code);
	vector<Object_Sub> search_ob(string str);
	vector<Object_Sub> search_ob_by_id(int id);
	vector<Subject_Sub> search_sub();
	vector<Subject_Sub> search_sub(int code);
	vector<Subject_Sub> search_sub(string str);
	vector<Subject_Sub> search_sub_by_id(int id);
	vector<Student_ob_ans> search_ob_ans(int id, int code);
	vector<Student_ob_ans> search_ob_ans(int id);
	vector<Student_ob_ans> search_ob_ans();
	vector<Student_sub_ans> search_sub_ans(int id, int code);
	vector<Student_sub_ans> search_sub_ans(int id);
	vector<Student_sub_ans> search_sub_ans();
	vector<Student_score> search_student_score(int id, int order);
	vector<Student_score> search_student_score(int id);
	vector<Student_score> search_student_score();
	vector<Student_score> search_student_score_by_order(int order);
	vector<Exam> search_exam(int order);
	vector<Exam> search_exam();
	vector<ob_acc> search_ob_acc(int order, int code);
	vector<ob_acc> search_ob_acc(int order);
	vector<sub_acc> search_sub_acc(int order, int code);
	vector<sub_acc> search_sub_acc(int order);
	vector<exam_aver> search_exam_aver(int order);
	vector<exam_aver> search_exam_aver();
	vector<BanRecord> search_ban_record(int id);
	vector<BanRecord> search_ban_record_from(int id);
	vector<BanRecord> search_ban_record();

	void reset_pw(int id);
	bool verify(string username, string password);

	void give_score();
	void give_score(int id, int code, int score);
	void calculate_score(int id, int exam_order);

	string generate_sub_list(string subject);
	float get_rank(int id, int order);
	void get_acc(int exam_order);
	void get_average(int exam_order);

	int generate_sub_code(bool ob_or_sub);
	int generate_exam_order();

	int generate_student_id();

	int generate_teacher_id();

	MySQL()
	{
		init_sql();
	}
};

class Server_END
{
public:
	Server svr;
public:
	void GetLogin();
	void GetLoginPage();
	void GetStudentManagePage();
	void GetStudentPage();
	void GetSubjectManagePage();
	void GetTestwaitingPage();
	void GetExamPage();
	void GetGradePage();
	void GetExamManagePage();
	void GetExamGradePage();
	void GetChangePwPage();
	void GetPaperPage();
	void GetCorrectPage();
};

void listening();